import { Node, Prefab, instantiate, EventTouch } from 'cc';
import { ccclass } from '../decorators';
import { BaseUI } from './BaseUI';
import { app } from '../application';
import { BaseView } from './BaseView';

/**
 * - 作者: DoooReyn
 * - 日期: 2023.10.31
 * - 名称: BaseStack
 * - 描述: UI 栈基类
 */
@ccclass()
export class BaseStack extends BaseView {
    /** 事件类型 */
    public static readonly EventType = {
        Inited: 'inited',
        Deinited: 'deinited',
        ChildAdded: 'child-added',
        ChildRemoved: 'child-removed',
        Clicked: 'clicked',
    };

    /** 是否可交互 */
    protected _interactable: boolean = false;

    protected onLoad(): void {
        this.node.on(Node.EventType.CHILD_ADDED, this.onCheckInteractable, this);
        this.node.on(Node.EventType.CHILD_REMOVED, this.onCheckInteractable, this);
        this.node.on(Node.EventType.TOUCH_START, this.onTouchScreen, this);
        this.node.on(Node.EventType.TOUCH_MOVE, this.onTouchScreen, this);
        this.node.on(Node.EventType.TOUCH_END, this.onTouchScreen, this);
        this.node.on(Node.EventType.TOUCH_CANCEL, this.onTouchScreen, this);
        this.onCheckInteractable();
    }

    protected onDestroy(): void {
        this.node.off(Node.EventType.CHILD_ADDED, this.onCheckInteractable, this);
        this.node.off(Node.EventType.CHILD_REMOVED, this.onCheckInteractable, this);
        this.node.off(Node.EventType.TOUCH_START, this.onTouchScreen, this);
        this.node.off(Node.EventType.TOUCH_MOVE, this.onTouchScreen, this);
        this.node.off(Node.EventType.TOUCH_END, this.onTouchScreen, this);
        this.node.off(Node.EventType.TOUCH_CANCEL, this.onTouchScreen, this);
    }

    /** 当前层被点击时调用 */
    protected onTouchScreen(e: EventTouch) {
        if (this._interactable) {
            e.propagationStopped = true;
            e.propagationImmediateStopped = true;
            e.preventSwallow = false;
            if (e.type === Node.EventType.TOUCH_END) {
                const child = this.node.children[this.node.children.length - 1];
                child && child.emit(BaseStack.EventType.Clicked);
            }
            this.logger.debug('禁止触摸继续传递');
        } else {
            e.preventSwallow = true;
            this.logger.debug('允许触摸继续传递');
        }
    }

    /**
     * 检查是否允许交互
     * - 当孩子节点数量大于 0 时，不允许触摸继续传递
     */
    protected onCheckInteractable() {
        this._interactable = this.interactable;
    }

    /** 是否可交互 */
    public get interactable() {
        return this.node.children.length > 0;
    }

    /**
     * 添加 UI
     * @param path 资源路径
     * @param bundle 资源所属包
     * @param data 携带数据
     * @returns
     */
    public async add(path: string, bundle: string = 'resources', data?: any): Promise<void> {
        const children = this.node.children;
        for (let i = children.length - 1; i >= 0; i--) {
            let child = children[i];
            if ((child as any)._path === path) {
                this.logger.info('页面已打开', path);
                const ui = child.getComponent(BaseUI);
                ui && (await ui.onData(data));
                return;
            }
        }

        // 0.5 秒内不显示小 Loading
        this.scheduleOnce(this.showTinyLoading, 0.5);
        let time = Date.now();
        const prefab = await app.resLoader.loadOne({ path, bundle, type: Prefab });
        if (prefab && prefab.data.getComponent(BaseUI)) {
            this.logger.debug(`${bundle}/${path} 加载时间: ${(Date.now() - time) / 1000}`);
            this.unschedule(this.showTinyLoading);
            await app.view.hint.tinyLoading.hideLoading();
            const node = instantiate(prefab);
            (node as any)._path = path;
            const ui = node.getComponent(BaseUI)!;
            await ui.onWillAdd();
            this.node.addChild(node);
            await ui.onAdded();
            await ui.onInit();
            await ui.onData(data);
        } else {
            await app.view.hint.tinyLoading.hideLoading();
            this.logger.error('预制体不存在或无效', prefab);
        }
    }

    protected showTinyLoading() {
        app.view.hint.tinyLoading.showLoading();
    }

    /**
     * 移除 UI
     * @param ui UI 路径或实例
     */
    public async remove(ui: string | BaseUI) {
        const children = this.node.children;
        let path = ui instanceof BaseUI ? (ui.node as any)._path : ui;
        for (let i = children.length - 1; i >= 0; i--) {
            const child = children[i];
            if ((child as any)._path === path) {
                await this.removeByChild(child);
                break;
            }
        }
    }

    /** 移除所有 UI */
    public async removeAll() {
        const children = this.node.children;
        for (let i = children.length - 1; i >= 0; i--) {
            await this.removeByChild(children[i]);
        }
    }

    /**
     * 移除 UI 节点
     * @param child 待移除的 UI 节点
     */
    protected async removeByChild(child: Node) {
        const com = child.getComponent(BaseUI);
        if (com) {
            await com.onWillRemove();
            child.removeFromParent();
            await com.onRemoved();
            await com.onDeinit();
            child.destroy();
        }
    }

    public async exit(): Promise<void> {
        await this.removeAll();
        await super.exit();
    }
}
